package com.mjk.common.tools.logger;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import com.mjk.common.base.error.BizException;
import com.mjk.common.util.StrUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class BizLogger {

	private static final String LOGGER_MARK = "--[{}]";
	/**
	 * 日志集合
	 */
	public static final ThreadLocal<List<Object>> logStack = new ThreadLocal<>();

	private BizLogger(){}
	/**
	 * 排除记录的日志
	 */
	protected static final String[] EXCLUDE_LOG = new String[] {
			"Creating a new SqlSession",
			" was not registered for synchronization because synchronization is not active",
			"will not be managed by Spring",
			"Closing non transactional SqlSession"
	};

	public static  void putLog(ILoggingEvent event) {
		StringBuilder sb = new StringBuilder();
		if(CollectionUtils.isEmpty(event.getMDCPropertyMap())){
			return;
		}
		for(String exclude : EXCLUDE_LOG) {
			if(event.getMessage().contains(exclude)) {
				return;
			}
		}
		IThrowableProxy iThrowableProxy = event.getThrowableProxy();
		sb.append(new Timestamp(event.getTimeStamp())).append("  "); //异常产生时间
        if (event.getCallerData() != null && event.getCallerData().length > 0) {
            StackTraceElement stackTraceElement = event.getCallerData()[0];
            //异常产生类名称
            sb.append(stackTraceElement.getClassName()).append("  ");
            //异常产生方法名
            sb.append(stackTraceElement.getMethodName()).append("  ");
        }
       // sb.append(event.getMDCPropertyMap().values());
        //异常信息
        sb.append(event.getFormattedMessage()).append("  ");
        if (iThrowableProxy != null) {
            //异常类型
        	 sb.append(iThrowableProxy.getClassName()).append("  ");
            //异常堆栈跟踪信息
            sb.append(Arrays.toString(Arrays.copyOfRange(iThrowableProxy.getStackTraceElementProxyArray(), 0, 10)));
        }
        putLog(event.getLevel().toString(),sb.toString());
	}

	/**
	 * 日志放入 日志集合中
     */
	public static void putLog(String leavel ,Object obj) {
		if(ObjectUtils.isEmpty(logStack.get())){
			List<Object> logs = new ArrayList<>();
			logs.add(obj);
			logStack.set(logs);
		}else {
			List<Object> map = logStack.get();
			map.add(obj);
			logStack.set(map);
		}
	}

	public static RuntimeException warn(Object obj) {
		if(ObjectUtils.isEmpty(obj)) {
			return null;
		}
		String location = getLoggerInfo("",obj);

		if ((obj instanceof Exception)) {
			log.warn(location, obj);
			if ((obj instanceof RuntimeException obj1)) {
                return  obj1;
            }
		} else {
			log.warn(location, obj);
		}
		putLog(LogConstns.LogLeavel.WARN.getLeavel(), location+"-->"+obj);
		return null;
	}

	public static RuntimeException debug(Object obj) {
		if(ObjectUtils.isEmpty(obj)) {
			return null;
		}
		String location = getLoggerInfo("",obj);
		if ((obj instanceof Exception)) {
			log.debug(location, obj);
			if ((obj instanceof RuntimeException obj1)) {
                return obj1;
            }
		} else {
			log.debug(location , obj);
		}
		putLog(LogConstns.LogLeavel.DEBUG.getLeavel(), location+"-->"+obj);
		return null;
	}

	public static void debug(String tilte, Object obj) {
		if(ObjectUtils.isEmpty(obj)) {
			return;
		}
		String location = getLoggerInfo(tilte,obj);

		if ((obj instanceof Exception)) {
			log.debug(location, obj);
			if ((obj instanceof RuntimeException))
				return;
		} else {
			log.debug(location , obj);
		}
		putLog(LogConstns.LogLeavel.DEBUG.getLeavel(), location+"-->"+obj);
	}

	public static RuntimeException info(Object obj) {
		if(ObjectUtils.isEmpty(obj)) {
			return null;
		}
		String location = getLoggerInfo("",obj);

		if ((obj instanceof Exception)) {
			log.info(location, obj);
			if ((obj instanceof RuntimeException obj1)) {
                return obj1;
            }
		} else {
			log.info(location , obj);
		}
		//putLog(LogConstns.LogLeavel.INFO.getLeavel(), location+"-->"+obj);
		return null;
	}

	public static RuntimeException info(String tilte, Object obj) {
		if(ObjectUtils.isEmpty(obj)) {
			return null;
		}
		String location = getLoggerInfo(tilte,obj);
		if ((obj instanceof Exception)) {
			log.info(location, obj);
			if ((obj instanceof RuntimeException obj1)) {
                return obj1;
            }
		} else {
			log.info(location, obj);
		}
		//putLog(LogConstns.LogLeavel.INFO.getLeavel(), location+"-->"+obj);
		return null;
	}

	public static RuntimeException error(Object obj) {
		if(ObjectUtils.isEmpty(obj)) {
			return null;
		}
		String location =  getLoggerInfo("",obj);
		if ((obj instanceof Exception)) {
			log.error(location, obj);
			if ((obj instanceof RuntimeException obj1)) {
                return obj1;
            }
		} else {
			log.error(location , obj);
		}
		putLog(LogConstns.LogLeavel.ERROR.getLeavel(), location+"-->"+obj);
		return new BizException(obj.toString());
	}

	public static RuntimeException error(String title, Object obj) {
		String location = getLoggerInfo(title,obj);
		if ((obj instanceof Exception)) {
			log.error(location, obj);
			if (obj instanceof RuntimeException obj1) {
                return obj1;
            }
		} else {
			log.error(location , obj);
		}
		putLog(LogConstns.LogLeavel.ERROR.getLeavel(), location+"-->"+obj);
		return new BizException(obj.toString());
	}

	private static String getLoggerInfo(String title,Object obje){
		String location;
		StackTraceElement[] stacks = Thread.currentThread().getStackTrace();
		location = stacks[3].getClassName() + "." + stacks[3].getMethodName()
				+ "-[" + stacks[3].getLineNumber() + "]";
		return title + location+ LOGGER_MARK;
	}

	/**
	 * 获取堆栈的最顶层
     */
	public static String getLastStack(){
		StackTraceElement[] stacks = Thread.currentThread().getStackTrace();
		List<String> statckList = new ArrayList<>();
		for(int i =0 ;i<6 ; i++) {
			statckList.add(stacks[i].getClassName() + "." + stacks[i].getMethodName()
					+ "-[" + stacks[i].getLineNumber() + "]");
		}
		return StrUtils.join(statckList,"\r\n");
	}
}
